/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.appspot.trafficando.util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.xml.namespace.QName;
import org.apache.abdera.ext.geo.Position;
import org.apache.abdera.model.Element;
import org.apache.abdera.model.ExtensibleElement;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GlobalPosition;
import org.springframework.util.Assert;

/**
 *
 * @author Domenico Maria Giffone
 */
public class GeoHelper {

    private static final String GML_NS               = "http://www.opengis.net/gml/3.2";
    private static final QName QNAME_GML_DIST        = new QName(GML_NS, "distance", "gml");
    private static final GeodeticCalculator CALCULATOR = new GeodeticCalculator();


    /**
     * Calculate the distance between the specified point.
     *
     * @param point1
     *                The first JTS point
     * @param point2
     *                The second JTS point
     * @return The calculated distance
     */
    // TODO v2 unit helper
  /*  public static double distance(Point point1, Point point2) {
        Assert.isTrue(point1 != null && point2 != null,
                "Can not calculate distance for null point");

        // Unit<Length> targetUnit = (unit != null) ? unit : SI.METER;
        try {
            double distance = JTS.orthodromicDistance(point1.getCoordinate(),
                    point2.getCoordinate(), DefaultGeographicCRS.WGS84);
            return distance;
            // return SI.METER.getConverterTo(targetUnit).convert(distance);
        } catch (TransformException e) {
            throw new RuntimeException(e);
        }
    } */


    public static String distance(GlobalPosition start, GlobalPosition dest) {
        Assert.isTrue(start != null && dest != null,
                "Can not calculate distance for null point");
        Ellipsoid reference = Ellipsoid.WGS84;
        BigDecimal d = new BigDecimal(CALCULATOR.calculateGeodeticMeasurement(reference, start, dest).getPointToPointDistance());
        return d.setScale(0, RoundingMode.HALF_EVEN).toPlainString();

    }

//    public static Point toJTSPoint(org.apache.abdera.ext.geo.Point abderaPoint) {
//        return createPoint(abderaPoint.getCoordinate().getLongitude(), abderaPoint.getCoordinate().getLatitude());
//    }
//
//    public static Point toJTSPoint(org.apache.abdera.ext.geo.Coordinate abderaCoordinate) {
//        return createPoint(abderaCoordinate.getLongitude(), abderaCoordinate.getLatitude());
//    }
    
    public static GlobalPosition toGP(org.apache.abdera.ext.geo.Point abderaPoint) {
        Assert.notNull(abderaPoint, "Point can't be null!");
        Assert.notNull(abderaPoint.getCoordinate(), "Coordinate of point can't be null!");
        double elevation = (abderaPoint.getElevation() == null) ? 0 : abderaPoint.getElevation();
        return new GlobalPosition(abderaPoint.getCoordinate().getLatitude(),
                abderaPoint.getCoordinate().getLongitude(), 
                elevation);
    }

    public static GlobalPosition toGP(ExtensibleElement element) {
        Position[] positions = org.apache.abdera.ext.geo.GeoHelper.getPositions(element);
        for (Position pos : positions) {
            if (pos instanceof org.apache.abdera.ext.geo.Point) {
                org.apache.abdera.ext.geo.Point p = (org.apache.abdera.ext.geo.Point) pos;
                return toGP(p);
            }
        }
        throw new RuntimeException("Cannot localize entry: " + element);
    }

    public static String distance(org.apache.abdera.ext.geo.Point location, ExtensibleElement element) {
        return distance(toGP(location), toGP(element));
    }

    public static void addDistance(ExtensibleElement element, org.apache.abdera.ext.geo.Point location) {
        Element distance = element.addExtension(QNAME_GML_DIST);
        distance.setAttributeValue("uom", "metre");
        distance.setText(String.valueOf(distance(location, element)));
    }

    public static String getDistance(ExtensibleElement element) {
        return element.getExtension(QNAME_GML_DIST).getText();
    }

}
